// $Id: DQLTemplate.java 21 2011-09-15 20:04:51Z huijzer@gmail.com $
package lime.common;

import java.util.ArrayList;
import java.util.List;

import com.documentum.fc.client.DfQuery;
import com.documentum.fc.client.IDfCollection;
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfTypedObject;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.IDfId;
import com.documentum.fc.common.IDfTime;
import com.documentum.fc.common.IDfValue;

/**
 * This class simplifies the use of DQL and helps to avoid common errors. It
 * executes DQL queries or updates, initiating iteration over IDfCollections
 * results.
 */
public class DQLTemplate {

	/** Session with a Documentum repository. */
	private IDfSession session;

	/**
	 * Construct a new DQLTemplate.
	 */
	public DQLTemplate(IDfSession session) {
		this.session = session;
	}

	public final void query(final String dql, final int queryType, final IRowCallbackHandler rch) throws DfException {
		IDfCollection col = null;
		try {
			final IDfQuery query = new DfQuery(dql);
			col = query.execute(session, queryType);
			while (col.next()) {
				rch.processRow(col);
			}
		} finally {
			if (col != null) {
				col.close();
			}
		}
	}

	public final boolean queryForBoolean(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asBoolean();
	}

	public final double queryForDouble(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asDouble();
	}

	public final IDfId queryForId(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asId();
	}

	public final int queryForInteger(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asInteger();
	}

	public final String queryForString(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asString();
	}
	
	public final IDfTime queryForTime(final String dql) throws DfException {
		IDfValue value = this.queryForValue(dql);
		return value.asTime();
	}

	public final IDfValue queryForValue(final String dql) throws DfException {
		List<IDfValue> results = this.queryForValues(dql);
		return this.requiredSingleResult(results);
	}

	public final List<IDfValue> queryForValues(final String dql) throws DfException {
		final List<IDfValue> results = new ArrayList<IDfValue>();
		this.query(dql, IDfQuery.DF_READ_QUERY, new IRowCallbackHandler() {
			public void processRow(IDfCollection col) throws DfException {
				IDfValue value = col.getValueAt(0);
				results.add(value);
			}
		});
		return results;
	}

	public final List<IDfTypedObject> queryForObjects(final String dql) throws DfException {
		final List<IDfTypedObject> objects = new ArrayList<IDfTypedObject>();
		this.query(dql, IDfQuery.DF_READ_QUERY, new IRowCallbackHandler() {
			public void processRow(IDfCollection col) throws DfException {
				IDfTypedObject object = col.getTypedObject();
				objects.add(object);
			}
		});
		return objects;
	}

	/**
	 * Return a single result object from the given List. Throws an exception if
	 * 0 or more than 1 element found.
	 */
	private <T> T requiredSingleResult(List<T> results) throws DfException {
		if (results == null || results.isEmpty()) {
			throw new DfException("Should not be null or empty");
		}
		if (results.size() > 1) {
			throw new DfException("Should not be multiple results");
		}
		return results.get(0);
	}
}
